{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0c43207a-30a2-489f-9b2f-0a9b625dbe1a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import curses\n",
    "from random import randint\n",
    "\n",
    "def game(stdscr):\n",
    "    curses.curs_set(0) # 关闭光标闪烁效果\n",
    "    h = curses.LINES\n",
    "    w = curses.COLS\n",
    "    stdscr.timeout(200)   # 键盘无响应超时\n",
    "   \n",
    "    # 蛇身的初始长度为4, snake[0]为蛇头\n",
    "    snake = [(h//2, x) for x in range(w//2 - 2, w//2 + 2)]\n",
    "\n",
    "    # 移动方向：下，上，左，右\n",
    "    dirs = [(1, 0), (-1, 0), (0, -1), (0, 1)]\n",
    "    cur_dir = 2 # 初始移动方向为“左”\n",
    "    food = None # 食物位置\n",
    "\n",
    "    # 展示初始的蛇身\n",
    "    for node in snake:\n",
    "        stdscr.addch(node[0], node[1], \"+\")\n",
    "\n",
    "    # 游戏循环\n",
    "    while True:\n",
    "        key = stdscr.getch()\n",
    "        if key == 10: # 监听到回车键，退出游戏\n",
    "            break\n",
    "\n",
    "        # 监听到 左、右、下、上 键，改变蛇的移动方向 \n",
    "        if key in (curses.KEY_LEFT, curses.KEY_RIGHT, \\\n",
    "                   curses.KEY_DOWN, curses.KEY_UP):\n",
    "            cur_dir = key - curses.KEY_DOWN\n",
    "\n",
    "        # 如果食物被吃，重新生成食物，并展示\n",
    "        if not food:\n",
    "            food = (randint(0, h - 1), randint(0, w - 1))\n",
    "            while food in snake: # 食物不可生成蛇向上\n",
    "                food = (randint(0, h - 1), randint(0, w - 1))\n",
    "\n",
    "            stdscr.addch(food[0], food[1], \"*\", curses.A_BLINK)\n",
    "        \n",
    "        # 新蛇头：new_head = snake[0] + dirs[cur_dir]\n",
    "        new_head = (snake[0][0] + dirs[cur_dir][0],\n",
    "                    snake[0][1] + dirs[cur_dir][1]) \n",
    "\n",
    "        # 蛇头超出屏蔽或者撞到自己，则判失败\n",
    "        if new_head[0] < 0 or new_head[0] >= h \\\n",
    "            or new_head[1] < 0 or new_head[1] >= w \\\n",
    "            or new_head in snake:\n",
    "            break\n",
    "\n",
    "        # 把新蛇头放入蛇链中, 并显示\n",
    "        snake.insert(0, new_head)\n",
    "        stdscr.addch(new_head[0], new_head[1], \"+\")\n",
    "\n",
    "        if new_head == food:\n",
    "            # 吃到食物，则蛇尾不移动，相当于吃到食物放到食尾\n",
    "            food = None\n",
    "        else:\n",
    "            # 未吃到食物，则将蛇尾弹出，并从屏幕中擦除\n",
    "            tail = snake.pop()\n",
    "            stdscr.addch(tail[0], tail[1], \" \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "dfe44787-5296-4421-ad39-fbca61e74758",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b)0\u001b7\u001b[?47h\u001b[1;24r\u001b[m\u001b[4l\u001b[?1l\u001b>"
     ]
    },
    {
     "ename": "error",
     "evalue": "nocbreak() returned ERR",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31merror\u001b[0m                                     Traceback (most recent call last)",
      "File \u001b[0;32m~/DevEnv/python/py39/lib/python3.9/curses/__init__.py:78\u001b[0m, in \u001b[0;36mwrapper\u001b[0;34m(func, *args, **kwds)\u001b[0m\n\u001b[1;32m     77\u001b[0m noecho()\n\u001b[0;32m---> 78\u001b[0m \u001b[43mcbreak\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     80\u001b[0m \u001b[38;5;66;03m# In keypad mode, escape sequences for special keys\u001b[39;00m\n\u001b[1;32m     81\u001b[0m \u001b[38;5;66;03m# (like the cursor keys) will be interpreted and\u001b[39;00m\n\u001b[1;32m     82\u001b[0m \u001b[38;5;66;03m# a special value like curses.KEY_LEFT will be returned\u001b[39;00m\n",
      "\u001b[0;31merror\u001b[0m: cbreak() returned ERR",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31merror\u001b[0m                                     Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[5], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;66;03m# 启动游戏\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m \u001b[43mcurses\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwrapper\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgame\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/DevEnv/python/py39/lib/python3.9/curses/__init__.py:100\u001b[0m, in \u001b[0;36mwrapper\u001b[0;34m(func, *args, **kwds)\u001b[0m\n\u001b[1;32m     98\u001b[0m stdscr\u001b[38;5;241m.\u001b[39mkeypad(\u001b[38;5;241m0\u001b[39m)\n\u001b[1;32m     99\u001b[0m echo()\n\u001b[0;32m--> 100\u001b[0m \u001b[43mnocbreak\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    101\u001b[0m endwin()\n",
      "\u001b[0;31merror\u001b[0m: nocbreak() returned ERR"
     ]
    }
   ],
   "source": [
    "# 启动游戏\n",
    "curses.wrapper(game)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ec86b81-e18a-44ab-a121-faf67928e0ad",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py39",
   "language": "python",
   "name": "py39"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
